package ru.surfstudio.graduate.bean;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;

import ru.surfstudio.graduate.R;
import ru.surfstudio.graduate.data.Channel;
import ru.surfstudio.graduate.data.Channel.Mark;
import ru.surfstudio.graduate.data.DatabaseHelper;
import ru.surfstudio.graduate.data.Feed;

import android.content.res.Resources;
import android.util.Log;

import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.misc.TransactionManager;
import com.j256.ormlite.stmt.DeleteBuilder;
import com.j256.ormlite.stmt.QueryBuilder;

/**
 *	Класс, инкапсулирующий работу с DAO объектами, реализует поддержку транзакций
 */
public class ChannelManager {
	
	private static final String TAG = "ChannelBean";
	
	private DatabaseHelper helper;
	
	private Dao<Channel, Integer> channelDao;
	private Dao<Feed, Integer> feedDao;
	
	public ChannelManager(DatabaseHelper helper) throws SQLException  {
		this.helper = helper;
		channelDao = helper.getChannelDao();
		feedDao = helper.getFeedDao();
	}
	
	/** Создать новый channel в БД */
	public void createChannel(String name, String uri, Mark mark) throws SQLException {
		
		Channel channel = new Channel();
		channel.setName(name);
		channel.setUrl(uri);
		channel.setMark(mark);
		
		channelDao.create(channel);
			
	}
	
	/** Создать папки "Все записи" и "Избранное", доступные по умолчанию 
	 * 	и хранящиеся в БД как channel */
	public void createDefaultChannels(Resources res) throws SQLException {
		if (channelDao.countOf() == 0) {
			createChannel(res.getString(R.string.channel_all), null, Mark.ALL);
			createChannel(res.getString(R.string.favorite), null, 
					Mark.FAVORITE);
		}
		setCountToDefaultChannels();
	}
	
	public List<Channel> getAllChannels() throws SQLException {
		return channelDao.queryForAll();
	}
	
	public List<Feed> getAllFeeds() throws SQLException {
		QueryBuilder<Feed, Integer> builder = feedDao.queryBuilder();
		builder.orderBy(Feed.DATE_FIELD_NAME, false);
		
		return feedDao.query(builder.prepare());
	}
	
	/** Веруть все непрочитанные feed */
	public List<Feed> getAllNewFeeds() throws SQLException {
		QueryBuilder<Feed, Integer> builder = feedDao.queryBuilder();
		builder.orderBy(Feed.DATE_FIELD_NAME, false).where().eq(Feed.READED_FIELD_NAME, false);
		
		return feedDao.query(builder.prepare());
	}
	
	/** Веруть все избранные feed */
	public List<Feed> getAllFavoriteFeeds() throws SQLException {
		QueryBuilder<Feed, Integer> builder = feedDao.queryBuilder();
		builder.orderBy(Feed.DATE_FIELD_NAME, false).where().eq(Feed.FAVORITE_FIELD_NAME, true);
		
		return feedDao.query(builder.prepare());
	}
	
	/** Веруть все избранные непрочитанные feed */
	public List<Feed> getNewFavoriteFeeds() throws SQLException {
		QueryBuilder<Feed, Integer> builder = feedDao.queryBuilder();
		builder.orderBy(Feed.DATE_FIELD_NAME, false).where()
				.eq(Feed.FAVORITE_FIELD_NAME, true).and().eq(Feed.READED_FIELD_NAME, false);
		
		return feedDao.query(builder.prepare());
	} 
	
	public boolean channelExists(Channel cnannel) throws SQLException {
		return !channelDao.queryForEq(Channel.URL_FIELD_NAME, cnannel.getUrl()).isEmpty();
	}
	
	public Channel getChannelForUrl(String url) throws SQLException {
		return channelDao.queryForEq(Channel.URL_FIELD_NAME, url).get(0);
	}
	
	/** Создать в БД новый channel и привязанные к нему feeds. 
	 * 	Если такой channel уже существует, то сохранить только те feeds,
	 * 	которых еще нет в БД */
	public Channel createChannelWithFeeds(Channel channel, List<Feed> feedList) 
			throws SQLException {
				
		boolean isChannelExist = channelExists(channel);
		if (!isChannelExist) {
			
			channel.setCount(feedList.size());
			channel.setUpdateDate(new Date());
			channelDao.create(channel);
			Log.d(TAG, "create Channel");
		} else {
			channel = getChannelForUrl(channel.getUrl());
		}
		
		List<Feed> newFeeds = updateFeeds(channel, feedList);
		if (isChannelExist) {
			Log.d(TAG, "last count: " + channel.getCount());
			Log.d(TAG, "new count: " + newFeeds.size());
			channel.setCount(channel.getCount() + newFeeds.size());
			channel.setUpdateDate(new Date());
			channelDao.update(channel);
			
			Log.d(TAG, "update Channel");
		}
		return channel;
	}
	
	/**	Добавить новые feeds в БД, принадлежацие конкретному channel. 
	 * 	Добавляются только новые feeds. */
	public List<Feed> updateFeeds(Channel channel, List<Feed> feedList) throws SQLException {
		List<Feed> result = new ArrayList<Feed>();
		for (Feed feed : feedList) {
			
			if (feedDao.queryForEq(Feed.URL_FIELD_NAME, feed.getUrl()).isEmpty()) {
				
				feed.setChannelId(channel.getId());
				feed.setChannelName(channel.getName());
				feedDao.create(feed);
				
				Log.d(TAG, "create feed: " + feed.getName());
				result.add(feed);
			}
		}
		return result;
	}
	
	/** Вернуть все feeds по channel id */
	public List<Feed> getAllFeedsByChannel(int id) throws SQLException {
		Log.d(TAG, "Channel ID:  " + id);
		QueryBuilder<Feed, Integer> builder = feedDao.queryBuilder();
		builder.orderBy(Feed.DATE_FIELD_NAME, false).where().eq(Feed.CHANNEL_ID_FIELD_NAME, id);
		
		return feedDao.query(builder.prepare());
	}
	
	/** Вернуть все непрочитанные feeds по channel id */
	public List<Feed> getNewFeedsByChannel(int id) throws SQLException {
		QueryBuilder<Feed, Integer> builder = feedDao.queryBuilder();
		builder.orderBy(Feed.DATE_FIELD_NAME, false).where().eq(Feed.CHANNEL_ID_FIELD_NAME, id)
			.and().eq(Feed.READED_FIELD_NAME, false);
		
		return feedDao.query(builder.prepare());
	}
	
	/** Вернуть channel по-умолчанию: "Все записи" */
	public Channel getChannelAll() throws SQLException {
		return channelDao.queryForEq(Channel.MARK_FIELD_NAME, Mark.ALL).get(0);
	}
	
	/** Вернуть channel по-умолчанию: "Избранное" */
	public Channel getChannelFavorite() throws SQLException {
		return channelDao.queryForEq(Channel.MARK_FIELD_NAME, Mark.FAVORITE).get(0);
	}
	
	/** Посчитать и присвоить количество feeds у channels по-умолчанию */
	public void setCountToDefaultChannels() throws SQLException {
		Channel channelAll = getChannelAll();
		channelAll.setCount((int) feedDao.countOf());
		channelDao.update(channelAll);
		
		Channel channelFavorite = getChannelFavorite();
		String query = "select count(f.id) from Feed as f where f.favorites = 1";
		List<String[]> resFeed = feedDao.queryRaw(query).getResults();
		
		int count = 0;
		if (!resFeed.isEmpty() && resFeed.get(0).length > 0)
			count = Integer.parseInt(resFeed.get(0)[0]);
		channelFavorite.setCount(count);
		
		channelDao.update(channelFavorite);
	}
	
	/** Удалить channel с прикрепленными к нему feeds */
	public void removeChannelWithFeeds(final Channel channel) throws SQLException {
		final DeleteBuilder<Feed, Integer> builder = feedDao.deleteBuilder();
		builder.where().eq(Feed.CHANNEL_ID_FIELD_NAME, channel.getId());
		
		TransactionManager tm = new TransactionManager(helper.getConnectionSource());
		tm.callInTransaction(new Callable<Void>() {
			@Override
			public Void call() throws Exception {
				feedDao.delete(builder.prepare());
				channelDao.delete(channel);
				return null;
			}
		
		});
		
	}
	
	public Feed findFeed(int id) throws SQLException {
		return feedDao.queryForId(id);
	}
	
	public void updateFeed(Feed feed) throws SQLException {
		feedDao.update(feed);
	}

}
